// SPDX-License-Identifier: GPL-2.0
/*
 * SCHEDULE: TASK STATE [RSDTZXKW]
 *
 * (C) 2024.05.19 BiscuitOS <buddy.zhang@aliyun.com>
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/mm.h>

#define DEV_NAME		"BiscuitOS"
#define BISCUITOS_IO		0xAE
#define BISCUITOS_RUN		_IO(BISCUITOS_IO, 0x01)

static struct task_struct *kthread;
static DECLARE_WAIT_QUEUE_HEAD(BiscuitOS_wq);
static int condition = 0;

static long BiscuitOS_ioctl(struct file *filp,
                        unsigned int ioctl, unsigned long arg)
{
	switch (ioctl) {
	case BISCUITOS_RUN:
		if (!condition) {
			condition = 1;
			wake_up_interruptible(&BiscuitOS_wq);
		}
		break;
	}
	return 0;
}

static struct file_operations BiscuitOS_fops = {
	.owner		= THIS_MODULE,
	.unlocked_ioctl	= BiscuitOS_ioctl,
};

static struct miscdevice BiscuitOS_drv = {
	.minor	= MISC_DYNAMIC_MINOR,
	.name	= DEV_NAME,
	.fops	= &BiscuitOS_fops,
};

static int kthread_fn(void *data)
{
	while (!kthread_should_stop()) {
		struct page *page;

		wait_event_interruptible(BiscuitOS_wq, condition);

		page  = alloc_page(GFP_KERNEL);
		if (!page)
			return -ENOMEM;

		printk("KTHREAD-ALLOC: %#lx\n", page_to_pfn(page));

		if (condition)
			condition = 0;

		printk("KTHREAD ENTRY SLEEPING[S]\n");
		// SLEEP
		set_current_state(TASK_INTERRUPTIBLE);
		schedule_timeout(msecs_to_jiffies(20000));
		// RUNNING
		__set_current_state(TASK_RUNNING);
		printk("KTHREAD ENTRY RUNNING[R]\n");
	}

	printk("KTHREAD HAS STOPPING\n");

	return 0;
}

static int __init BiscuitOS_init(void)
{
	misc_register(&BiscuitOS_drv);

	kthread = kthread_create(kthread_fn, NULL, "BiscuitOS-kthread");
	if (!kthread) {
		printk("ERROR: BAD KTHREAD.\n");
		return -EINVAL;
	}

	/* WAK UP KTHREAD */
	wake_up_process(kthread);
	return 0;
}

static void __exit BiscuitOS_exit(void)
{
	misc_deregister(&BiscuitOS_drv);
}

module_init(BiscuitOS_init);
module_exit(BiscuitOS_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("BiscuitOS <buddy.zhang@aliyun.com>");
MODULE_DESCRIPTION("BiscuitOS SCHEDULE SUBSYSTEM");
